﻿using System;
using System.Collections.Generic;
using System.Reflection;
using IInject.Registration;
using IInject.Registration.Scanning;
using IInject.Resolution;
using IInject.Resolution.Activation;

namespace IInject
{
	///<summary>
	/// Provides an interface for registering and resolving types from a <see cref="Container"/>.
	///</summary>
	public interface IContainer
	{
		#region Properties

		///<summary>
		/// Gets the <see cref="IRegistryProvider"/> responsible for registering all types.
		///</summary>
		IRegistryProvider Registry { get; }
		///<summary>
		/// Gets the <see cref="ITypeDependencyResolver"/> responsible for resolving all dependencies.
		///</summary>
		ITypeDependencyResolver Resolver { get; }
		///<summary>
		/// Gets the <see cref="IDependencyActivator"/> responsible for creating all dependencies.
		///</summary>
		ITypeDependencyActivator Activator { get; }

		#endregion

		#region Methods

		#region Assembly Scanning

		/// <summary>
		/// Loads and scans the assembly with the provided name.
		/// </summary>
		/// <param name="assemblyName">The name of the assembly to scan.</param>
		IContainer Scan(string assemblyName);
		/// <summary>
		/// Loads and scans the assembly with the provided <see cref="AssemblyName"/>.
		/// </summary>
		/// <param name="assemblyName">The <see cref="AssemblyName"/> of the assembly to scan.</param>
		IContainer Scan(AssemblyName assemblyName);
		/// <summary>
		/// Scans the provided <see cref="Assembly"/>.
		/// </summary>
		/// <param name="assembly">The <see cref="Assembly"/> to scan.</param>
		IContainer Scan(Assembly assembly);

		/// <summary>
		/// Gives access to an AssemblyScanner that handles scanning assemblies.
		/// This allows for customizing each scan by providing <see cref="IScanningRule"/>s
		/// or just using the default <see cref="ScanRules"/>.
		/// </summary>
		/// <param name="scanner">The delegate using the AssemblyScanner</param>
		/// <returns><see cref="IContainer"/></returns>
		IContainer Scan(Action<IAssemblyScanner> scanner);

		#endregion

		#region Get Methods

		///<summary>
		/// Injects the dependencies into the type and returns the initialized object.
		///</summary>
		///<typeparam name="T">The registered type <typeparamref name="T"/>.</typeparam>
		///<returns>The resolved type <see cref="T"/>.</returns>
		T Get<T>();
		///<summary>
		/// Injects the dependencies into the type and returns the initialized object.
		///</summary>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<typeparam name="T">The registered type <typeparamref name="T"/>.</typeparam>
		///<returns>The resolved type <see cref="T"/>.</returns>
		T Get<T>(string key);
		///<summary>
		/// Injects the dependencies into the type and returns the initialized object.
		///</summary>
		///<param name="type">The type to return.</param>
		///<returns>The resolved type object.</returns>
		object Get(Type type);
		///<summary>
		/// Injects the dependencies into the type and returns the initialized object.
		///</summary>
		///<param name="type">The registered <see cref="Type"/>.</param>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<returns>The resolved type.</returns>
		object Get(Type type, string key);

		///<summary>
		/// Injects the dependencies into the types and returns them.
		///</summary>
		///<typeparam name="T">The registered types <typeparamref name="T"/>.</typeparam>
		///<returns>The resolved types <see cref="T"/>.</returns>
		IEnumerable<T> GetAll<T>();
		///<summary>
		/// Injects the dependencies into the types and returns them.
		///</summary>
		///<typeparam name="T">The registered types <typeparamref name="T"/>.</typeparam>
		///<returns>The resolved types <see cref="T"/>.</returns>
		IEnumerable<T> GetAll<T>(string key);
		///<summary>
		/// Injects the dependencies into the types and returns them.
		///</summary>
		///<param name="type"></param>
		///<returns>The resolved types.</returns>
		IEnumerable<object> GetAll(Type type);

		/// <summary>
		///  Injects the dependencies into the types and returns them.
		/// </summary>
		/// <param name="type"></param>
		/// <param name="key"></param>
		/// <returns>The resolved types.</returns>
		IEnumerable<object> GetAll(Type type, string key);

		#endregion

		#region Map Methods

		///<summary>
		///</summary>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns>An <see cref="IContextBuilder"/> used to configure and register the type.</returns>
		IContextBuilder<T> Map<T>();
		///<summary>
		///</summary>
		///<typeparam name="TFrom">The type to register.</typeparam>
		///<typeparam name="TTo">The type returned when resolved.</typeparam>
		///<returns>An <see cref="IContextBuilder"/> used to configure and register the type.</returns>
		IContextBuilder<TFrom> Map<TFrom, TTo>() where TTo : class, TFrom;
		///<summary>
		///</summary>
		///<param name="type"></param>
		///<returns>An <see cref="IContextBuilder"/> used to configure and register the type.</returns>
		IContextBuilder Map(Type type);

		/// <summary>
		/// </summary>
		/// <param name="registerAction"></param>
		/// <returns>An <see cref="IContextBuilder"/> used to configure and register the type.</returns>
		IContainer Map(Action<IContextBuilder> registerAction);
		///<summary>
		///</summary>
		///<param name="registerAction">The <see cref="Action"/> used to configure and register the type.</param>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns>An <see cref="IContextBuilder"/> used to configure and register the type.</returns>
		IContainer Map<T>(Action<IContextBuilder<T>> registerAction);

		#endregion

		#region Set Generic Methods

		///<summary>
		///</summary>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns></returns>
		IContainer Set<T>() where T : class;
		///<summary>
		///</summary>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns></returns>
		IContainer Set<T>(string key) where T : class;
		///<summary>
		///</summary>
		///<param name="scope">The <see cref="Scope"/> of how this type will be resolved.</param>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns></returns>
		IContainer Set<T>(Scope scope) where T : class;
		///<summary>
		///</summary>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<param name="scope">The <see cref="Scope"/> of how this type will be resolved.</param>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns></returns>
		IContainer Set<T>(string key, Scope scope) where T : class;
		///<summary>
		///</summary>
		///<param name="instance"></param>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns></returns>
		IContainer Set<T>(T instance) where T : class;
		///<summary>
		///</summary>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<param name="instance"></param>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns></returns>
		IContainer Set<T>(string key, T instance) where T : class;
		///<summary>
		///</summary>
		///<param name="instance"></param>
		///<param name="scope">The <see cref="Scope"/> of how this type will be resolved.</param>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns></returns>
		IContainer Set<T>(T instance, Scope scope) where T : class;
		///<summary>
		///</summary>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<param name="instance"></param>
		///<param name="scope">The <see cref="Scope"/> of how this type will be resolved.</param>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns></returns>
		IContainer Set<T>(string key, T instance, Scope scope) where T : class;

		///<summary>
		///</summary>
		///<typeparam name="TFrom"></typeparam>
		///<typeparam name="TTo"></typeparam>
		///<returns></returns>
		IContainer Set<TFrom, TTo>() where TTo : class, TFrom;
		///<summary>
		///</summary>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<typeparam name="TFrom"></typeparam>
		///<typeparam name="TTo"></typeparam>
		///<returns></returns>
		IContainer Set<TFrom, TTo>(string key) where TTo : class, TFrom;
		///<summary>
		///</summary>
		///<param name="scope">The <see cref="Scope"/> of how this type will be resolved.</param>
		///<typeparam name="TFrom"></typeparam>
		///<typeparam name="TTo"></typeparam>
		///<returns></returns>
		IContainer Set<TFrom, TTo>(Scope scope) where TTo : class, TFrom;
		///<summary>
		///</summary>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<param name="scope">The <see cref="Scope"/> of how this type will be resolved.</param>
		///<typeparam name="TFrom"></typeparam>
		///<typeparam name="TTo"></typeparam>
		///<returns></returns>
		IContainer Set<TFrom, TTo>(string key, Scope scope) where TTo : class, TFrom;

		///<summary>
		///</summary>
		///<param name="items"></param>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns></returns>
		IContainer SetAll<T>(IEnumerable<T> items);
		///<summary>
		///</summary>
		///<param name="items"></param>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns></returns>
		IContainer SetAll<T>(string key, IEnumerable<T> items);
		///<summary>
		///</summary>
		///<param name="from"></param>
		///<param name="items"></param>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns></returns>
		IContainer SetAll<T>(Type from, string key, IEnumerable<T> items);
		///<summary>
		///</summary>
		///<param name="registerAction"></param>
		///<typeparam name="T">The type to register.</typeparam>
		///<returns></returns>
		IContainer SetAll<T>(Action<IEnumerableContextBuilder<T>> registerAction);
		///<summary>
		///</summary>
		///<param name="items"></param>
		///<typeparam name="TFrom"></typeparam>
		///<typeparam name="TTo"></typeparam>
		///<returns></returns>
		IContainer SetAll<TFrom, TTo>(IEnumerable<TTo> items) where TTo : TFrom;
		///<summary>
		///</summary>
		///<param name="items"></param>
		///<typeparam name="TFrom"></typeparam>
		///<typeparam name="TTo"></typeparam>
		///<returns></returns>
		IContainer SetAll<TFrom, TTo>(string key, IEnumerable<TTo> items) where TTo : TFrom;
		///<summary>
		///</summary>
		///<param name="registerAction"></param>
		///<typeparam name="TFrom"></typeparam>
		///<typeparam name="TTo"></typeparam>
		///<returns></returns>
		IContainer SetAll<TFrom, TTo>(Action<IEnumerableContextBuilder<TTo>> registerAction);

		#endregion

		#region Set Non-Generic Methods

		///<summary>
		///</summary>
		///<param name="type"></param>
		///<returns></returns>
		IContainer Set(Type type);
		///<summary>
		///</summary>
		///<param name="type"></param>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<returns></returns>
		IContainer Set(Type type, string key);
		///<summary>
		///</summary>
		///<param name="type"></param>
		///<param name="scope">The <see cref="Scope"/> of how this type will be resolved.</param>
		///<returns></returns>
		IContainer Set(Type type, Scope scope);
		///<summary>
		///</summary>
		///<param name="type"></param>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<param name="scope">The <see cref="Scope"/> of how this type will be resolved.</param>
		///<returns></returns>
		IContainer Set(Type type, string key, Scope scope);
		///<summary>
		///</summary>
		///<param name="type"></param>
		///<param name="instance"></param>
		///<returns></returns>
		IContainer Set(Type type, object instance);
		///<summary>
		///</summary>
		///<param name="type"></param>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<param name="instance"></param>
		///<returns></returns>
		IContainer Set(Type type, string key, object instance);
		///<summary>
		///</summary>
		///<param name="type"></param>
		///<param name="instance"></param>
		///<param name="scope">The <see cref="Scope"/> of how this type will be resolved.</param>
		///<returns></returns>
		IContainer Set(Type type, object instance, Scope scope);
		///<summary>
		///</summary>
		///<param name="type"></param>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<param name="instance"></param>
		///<param name="scope">The <see cref="Scope"/> of how this type will be resolved.</param>
		///<returns></returns>
		IContainer Set(Type type, string key, object instance, Scope scope);

		///<summary>
		///</summary>
		///<param name="from"></param>
		///<param name="to"></param>
		///<returns></returns>
		IContainer Set(Type from, Type to);
		///<summary>
		///</summary>
		///<param name="from"></param>
		///<param name="to"></param>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<returns></returns>
		IContainer Set(Type from, Type to, string key);
		///<summary>
		///</summary>
		///<param name="from"></param>
		///<param name="to"></param>
		///<param name="scope">The <see cref="Scope"/> of how this type will be resolved.</param>
		///<returns></returns>
		IContainer Set(Type from, Type to, Scope scope);
		///<summary>
		///</summary>
		///<param name="from"></param>
		///<param name="to"></param>
		///<param name="key">The key associated with the registered type, null if no key is required.</param>
		///<param name="scope">The <see cref="Scope"/> of how this type will be resolved.</param>
		///<returns></returns>
		IContainer Set(Type from, Type to, string key, Scope scope);

		#endregion

		#endregion
	}
}